﻿using System;
using System.Collections.Generic;
using System.Linq;
using LogManager.Base;
using LogManager.Base.Data;
using Raven.Client;
using Raven.Client.Document;

namespace LogManager.LocalStorage.Data
{
    public class LocalImportService : IImportService
    {
        private readonly IDocumentStore _store;

        public LocalImportService()
        {
            _store = new DocumentStore
                         {
                             Url = "http://localhost:9000",
                         };
            _store.Initialize();
        }

        public IEnumerable<EventImport> All()
        {
            using (var session = _store.OpenSession())
            {
                return session.Query<EventImport>().OrderByDescending(ei => ei.ImportDate);
            }
        }

        public IEnumerable<EventImport> All(string serverName, string logName)
        {
            using (var session = _store.OpenSession())
            {
                return session.Query<EventImport>()
                    .Where(i => i.ServerName == serverName && i.LogName == logName)
                    .OrderByDescending(ei => ei.ImportDate);
            }
        }

        public IEnumerable<IEventImport> WithinRange(DateTime start, DateTime end, string serverName, string logName)
        {
            using (var session = _store.OpenSession())
            {
                return session.Query<EventImport>()
                    .Where(i => i.ServerName == serverName && i.LogName == logName)
                    .Where(i => i.Start > start && i.End < end )
                    .OrderBy(i => i.Start)//Imports within range
                    .AsEnumerable();
            }
        }

        public IEventImport Interleaving(DateTime start, DateTime end, string serverName, string logName)
        {
            using (var session = _store.OpenSession())
            {
                return session.Query<EventImport>()
                    .Where(i => i.ServerName == serverName && i.LogName == logName)
                    .Where(i => i.Start < start && i.End < end)
                    .SingleOrDefault();
            }
        }
        
        private bool HasAnyImports(DateTime start, DateTime end, string serverName,string logName)
        {
            using (var session = _store.OpenSession())
            {
                return session.Query<EventImport>()
                    .Where(i => i.ServerName == serverName && i.LogName == logName && ((i.Start > start && i.Start < end) || (i.End > start && i.End < end)))
                    .Count() > 0;
            }
        }

        private bool HasRecentImport(DateTime start, DateTime end, string serverName, string logName, TimeSpan limit)
        {
            var matches = All(serverName, logName).ToList().Where(i => start - i.Start < limit && end - i.End < limit);
            return matches.Count() > 0;         
        }

        public IEventImport Leaving(DateTime start, DateTime end,string serverName, string logName)
        {
            using (var session = _store.OpenSession())
            {
                return session.Query<EventImport>()
                    .Where(i => i.ServerName == serverName && i.LogName == logName)
                    .Where(i => i.Start > start && i.End > end)
                    .SingleOrDefault();
            }
        }

        public IEventImport CoversRange(DateTime start, DateTime end,string serverName, string logName)
        {
            using (var session = _store.OpenSession())
            {
                return session.Query<EventImport>()
                    .Where(i => i.ServerName == serverName && i.LogName == logName)
                    .Where(i => i.Start < start && i.End > end)
                    .SingleOrDefault();
            }
        }

        public IEnumerable<IPlannedEventImport> RequiredImports(DateTime start, DateTime end,string serverName, string logName)
        {
            var reqImports = new List<EventImport>();

            ////Check if there was a sync for this log and server before 5 minutes that started and ended not too much before
            //if (HasRecentImport(start, end, serverName, logName, new TimeSpan(0, 5, 0)))
            //    return reqImports;

            if (!HasAnyImports(start, end, serverName, logName))
            {
                reqImports.Add(new EventImport { Start = start, End = end, ServerName = serverName, LogName = logName });
                return reqImports;
            }


            if (CoversRange(start, end, serverName, logName) != null)
                return reqImports;

            var interleaving = Interleaving(start, end,serverName,logName);
            var leaving = Leaving(start, end, serverName, logName);
            var within = WithinRange(start, end, serverName, logName).ToList();

            if (within.Count > 0)
            {

                for (int i = 0; i < within.Count; i++)
                {
                    //for first add one for interleaving
                    if (i == 0)
                        reqImports.Add(new EventImport
                                           {
                                               Start = interleaving == null ? start : interleaving.End,
                                               End = within[i].End,
                                               ServerName = serverName,
                                               LogName = logName
                                           });
                        //for last
                    else if (i == within.Count - 1)
                        reqImports.Add(new EventImport
                                           {
                                               Start = within[i].End,
                                               End = leaving == null ? end : leaving.Start,
                                               ServerName = serverName,
                                               LogName = logName
                                           });
                    else
                        reqImports.Add(new EventImport
                                           {
                                               Start = within[i - 1].End,
                                               End = within[i].Start,
                                               ServerName = serverName,
                                               LogName = logName
                                           });
                }

            }
            else
            {
                if(interleaving != null && leaving != null)
                    reqImports.Add(new EventImport
                    {
                        Start = interleaving.End,
                        End = leaving.Start,
                        ServerName = serverName,
                        LogName = logName
                    });
                else if (interleaving != null)
                    reqImports.Add(new EventImport
                    {
                        Start = interleaving.End,
                        End = end,
                        ServerName = serverName,
                        LogName = logName
                    });
                
                else if (leaving != null)
                    reqImports.Add(new EventImport
                    {
                        Start = start,
                        End = leaving.Start,
                        ServerName = serverName,
                        LogName = logName
                    });
            }

            return reqImports;
        }

        public void Dispose()
        {
            _store.Dispose();
        }
    }
}
